﻿using System;
using System.Collections.Generic;
#if SCRIPTSHARP
using System.Html;
using System.Serialization;
#else
using System.Text;
using System.Web.Script.Serialization;
#endif
using System.Collections;
using System.Reflection;



namespace Db7
{
    public class CompactJSONSerializer
    {
        public List<ListSerializer> Lists;
        public List<ListSerializer> ListsNN
        {
            get
            {
                if (Lists == null) Lists = new List<ListSerializer>();
                return Lists;
            }
        }

        public List<PropInf> Props;

        public List<PropInf> PropsNN
        {
            get
            {
                if (Props == null) Props = new List<PropInf>();
                return Props;
            }
        }
 

#if SCRIPTSHARP
        public void Deserialize(Dictionary Source, Dictionary Destination, object destObj)
        {
            this.Lists = Source["Lists"] as List<ListSerializer>;
            if (this.Lists != null)
            {
                #region Merge Lists
                foreach (ListSerializer list in this.Lists)
                {
                    Array destArr = new Array(); Util.SetProperty(Destination, list.name, destArr, destObj);  //Destination[list.name] = destArr;
                    int ithRow = 0;
                    foreach (object[] row in list.data)
                    {
                        string classType = list.itemTypeString;
                        object itemObj = Util.CreateNew(classType);
                        destArr[ithRow] = itemObj; ithRow++;
                        Dictionary destDict = Dictionary.GetDictionary(itemObj);
                        int ithField = 0;
                        foreach (string field in list.fields)
                        {
                            Util.SetProperty(destDict, field, row[ithField], itemObj); ithField++;

                        }
                    }
                }
                #endregion
            }
            this.Props = Source["Props"] as List<PropInf>;
            if (this.Props != null)
            {
                #region Merge Props
                foreach (PropInf prop in this.Props)
                {
                    Util.SetProperty(Destination, prop.name, prop.value, destObj);
                }
                #endregion
            }
        }

        public string Serialize(object srcObj)
        {
            Dictionary srcDict = Dictionary.GetDictionary(srcObj);
            Array members = Util.GetAllMembers(srcObj);
            int len = members.Length;
            for(int i = 0; i < len; i++)
            {
                Array pair = members[i] as Array;
                string name = pair[0] as string;
                if (name.StartsWith("get_"))
                {
                    Script.Eval("debugger;");
                    PropInf prop = new PropInf();
                    this.PropsNN.Add(prop);
                    prop.name = name.Substr(4);
                    prop.value = Util.GetProperty(srcDict, prop.name, srcObj);
                }
            }
            return Json.Stringify(this);
        }
#else
        #region Server Side

        public void Deserialize(string compactJSON, object DestinationObj)
        {
            var jsonSerializer = new JavaScriptSerializer();
            CompactJSONSerializer compact = jsonSerializer.Deserialize<CompactJSONSerializer>(compactJSON);
            var propLookups = new Dictionary<string, PropertyInfo>();
            var props = DestinationObj.GetType().GetProperties();
            foreach (var prop in props)
            {
                string jsName = Util.ServerSidePropertyNameToClientSideGetName(prop);
                if (propLookups.ContainsKey(jsName))
                {
                    throw new Exception("Unable to distinguish between two properties with similar names");
                }
                propLookups[jsName] = prop;
            }
            foreach (var prop in compact.Props)
            {
                if (!propLookups.ContainsKey(prop.name))
                {
                    throw new Exception("Unable to find server side property with name " + prop.name);
                }
                var pi = propLookups[ prop.name ];
                pi.SetValue(DestinationObj, prop.value, null);
            }
        }

        public string Serialize(object obj)
        {
            var sb = new StringBuilder();
            Serialize(obj, sb);
            return sb.ToString();
        }

        public void Serialize(object obj, StringBuilder sb)
        {
            var properties = obj.GetType().GetProperties();
            foreach (var property in properties)
            {
                #region each property
                var propType = property.PropertyType;
                if (typeof(System.Collections.ICollection).IsAssignableFrom(propType))
                {
                    #region List property
                    var newList = new ListSerializer(); ListsNN.Add(newList);
                    newList.name = property.Name;
                    Type itemType = null;
                    if (propType.IsGenericType)
                    {
                        itemType = propType.GetGenericArguments()[0];
                    }
                    else
                    {
                        throw new NotImplementedException();
                    }
                    newList.itemTypeString = itemType.FullName;
                    var values = property.GetValue(obj, null) as IEnumerable;
                    var itemProperties = itemType.GetProperties();
                    List<string> fields = new List<string>();
                    foreach (var itemProperty in itemProperties)
                    {
                        fields.Add(itemProperty.Name);
                    }
                    newList.fields = fields.ToArray();
                    List<object[]> data = new List<object[]>();
                    foreach (var value in values)
                    {
                        List<object> row = new List<object>();
                        foreach (var itemProperty in itemProperties)
                        {
                            row.Add(itemProperty.GetValue(value, null));
                        }
                        data.Add(row.ToArray());
                    }
                    newList.data = data;
                    #endregion
                }
                else if(propType == typeof(string))
                {
                    var strProp = new PropInf
                    {
                        name = property.Name,
                        value = property.GetValue(obj, null),
                    };
                    this.PropsNN.Add(strProp);
                }

                #endregion
            }
            
            var json = new JavaScriptSerializer();
            json.Serialize(this, sb);

        }
        #endregion
#endif
    }

    public class ListSerializer
    {

        public string name;

        public string itemTypeString;

        public string[] fields;

        public List<object[]> data;
    }

    public class PropInf
    {
        public string name;

        public object value;

        string propTypeString;

        //TODO:  Constraints

    }


}
